/**
 * \brief   从摄像头获取YUV NV12
 * \note    播放YUV NV12的命令
 *          ffplay -f rawvideo -pix_fmt nv12 -video_size 1280x720 test.nv12
 * \note    为了减少编码时间，本模块没有使用缓存，而是直接简单的使用了一个锁；
 *          帧长度不是按参数动态分配，而是直接申请了一个固定长度的数组；后续优化
 *          代码时需要改掉这些机制
 */

#include "module.h"
#include "module_yuvin.h"
#include <string.h> /* memcpy */
#include <stdio.h>  /* printf fflush */

#define EPERM 1 /* 错误码：操作不允许，Operation not permitted */
#define pr_info printf
#define MDATA_LEN (2 * 1024 * 1024) /* 当前电脑视频1280x720 NV12 1.5字节每像素一帧1,382,400字节 */

static unsigned char mdata[MDATA_LEN];  /* 数据前几个字节是时间戳，后面跟着帧数据 */
int yuvmdatalen = 0;    /* 既作为长度，又作为消费者生产者的锁 */
static struct module *target = NULL;

int yuvin_module_register()
{
    yuvmdatalen = 0;    /* 释放锁 */

    return 0;
}

/**
 * \param flag: 0代表一帧开始，1代表一帧结束
 */
int yuvin_module_putdata(char *data, int len, int flag, int tick)
{
    static int totallen;

    /* 被调用者保证此时已得到锁才调用了此函数 */

    if (!data || len > MDATA_LEN)
        return -EPERM;

    if (flag == 0) { /* Qt QVideoFrame模块将一帧做两次发出的 */
        totallen = 0;
        memcpy(mdata + totallen, &tick, sizeof(int));   /* 将时间戳放到缓存中发送出去，单位ms */
        totallen += sizeof(int);
    }

    memcpy(mdata + totallen, data, len); /* 赋值数据（一帧要分多次赋予） */
    totallen += len;

    if (flag == 1)
        yuvmdatalen = totallen; /* 赋值长度并上锁，之后会在本模块run轮询中识别数据的到来然后进行发送 */

//    pr_info("yuv %d %d, ", totallen, yuvmdatalen);
//    fflush(stdout);

    return 0;
}

static int module_init(void)
{
    if (target)
        target->init();

    return 0;
}

static int module_start(void *param)
{
    start_param_t sparam = {"test.nv12", AVTYPE_H264};

    if (target)
        target->start(&sparam);

    return 0;
}

/**
 * \brief   模块一直轮询
 */
static int module_run(void)
{
    if (target)
        target->run();

    if (!yuvmdatalen)
        return 0;       /* 如果没有数据，未获取到锁 */
    /**!!! 为了简化代码的编写，当前数据每发送到一个模块都会重新拷贝一次，后续要将此机制改为缓存 */
    if (target)
        target->putdata(mdata, yuvmdatalen, 0); /* 如果有数据则发到下个模块 */

    yuvmdatalen = 0;    /* 处理完之后释放锁 */

    return 0;
}

static int module_stop(void)
{
    if (target)
        target->stop();

    return 0;
}

static int module_exit(void)
{
    if (target)
        target->exit();

    return 0;
}

static int module_settarget(void *mtarget)
{
    /* 这里默认的下一个模块是g_module_videocenc */
    target = (struct module *)mtarget;

    return 0;
}

static int module_putdata(void *data, int len, int flag)
{
    /* 被Qt C++模块直接调用yuvin_module_putdata接口来传送数据了 */
    return 0;
}

struct module g_module_yuvin = {
    "yuvin", module_init,
    module_start, module_run,
    module_stop, module_exit,
    module_settarget, module_putdata
};
